﻿using System.Reflection.Emit;
using Aeon.Emulator.DebugSupport;

namespace Aeon.Emulator.Decoding.Recompiler
{
    /// <summary>
    /// Contains information about the current state of an instruction recompiler.
    /// </summary>
    internal sealed class CompilerContext
    {
        #region Private Fields
        private LocalBuilder incrementer;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the CompilerContext class.
        /// </summary>
        /// <param name="counter">Instruction counter local variable.</param>
        /// <param name="maxCount">Maximum number of instructions to emulate.</param>
        /// <param name="registers">Current register code generator.</param>
        public CompilerContext(LocalBuilder counter, int maxCount, RegisterExpressions registers)
        {
            this.CounterInt = counter;
            this.MaxCount = maxCount;
            this.Registers = registers;
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the current instruction.
        /// </summary>
        public Instruction Instruction { get; private set; }
        /// <summary>
        /// Gets the current operand.
        /// </summary>
        public CodeOperand Operand { get; private set; }
        /// <summary>
        /// Gets the current operand size in bits.
        /// </summary>
        public int OperandSize { get; private set; }
        /// <summary>
        /// Gets the current address size in bits.
        /// </summary>
        public int AddressSize { get; private set; }
        /// <summary>
        /// Gets the instruction counter local variable.
        /// </summary>
        public LocalBuilder CounterInt { get; private set; }
        /// <summary>
        /// Gets the maximum number of instructions to emulate.
        /// </summary>
        public int MaxCount { get; private set; }
        /// <summary>
        /// Gets the flow direction of the current operand.
        /// </summary>
        public CodeOperandFlow FlowDirection { get; private set; }
        /// <summary>
        /// Gets the object uses to generate register reads and writes.
        /// </summary>
        public RegisterExpressions Registers { get; private set; }
        #endregion

        #region Public Methods
        /// <summary>
        /// Sets the current instruction context.
        /// </summary>
        /// <param name="instruction">New instruction context.</param>
        public void SetInstruction(Instruction instruction)
        {
            this.Instruction = instruction;
            var prefixes = instruction.Prefixes;
            this.OperandSize = (prefixes & PrefixState.OperandSize) != 0 ? 32 : 16;
            this.AddressSize = (prefixes & PrefixState.AddressSize) != 0 ? 32 : 16;
        }
        /// <summary>
        /// Sets the current operand context.
        /// </summary>
        /// <param name="operand">New operand context.</param>
        /// <param name="index">Index of the new operand.</param>
        public void SetOperand(CodeOperand operand, int index)
        {
            this.Operand = operand;
            this.FlowDirection = this.Instruction.Opcode.GetOperandFlowDirection(index);
            if(operand.OperandSize == CodeOperandSize.Byte)
                this.OperandSize = 8;
            else
                this.OperandSize = (Instruction.Prefixes & PrefixState.OperandSize) != 0 ? 32 : 16;
        }
        /// <summary>
        /// Gets a string incrementer local.
        /// </summary>
        /// <param name="il">Associated ILGenerator instance.</param>
        /// <returns>String incrementer local.</returns>
        public LocalBuilder GetIncrementer(ILGenerator il)
        {
            if(this.incrementer == null)
                this.incrementer = il.DeclareLocal(typeof(int));

            return this.incrementer;
        }
        #endregion
    }
}
